package com.redhat.redwork.coc;


import org.apache.commons.lang.StringUtils;
import org.apache.struts2.dispatcher.ServletActionRedirectResult;
import org.apache.struts2.dispatcher.mapper.ActionMapping;
import org.apache.struts2.views.freemarker.FreemarkerResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.opensymphony.xwork2.ActionChainResult;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ObjectFactory;
import com.opensymphony.xwork2.Result;
import com.opensymphony.xwork2.UnknownHandler;
import com.opensymphony.xwork2.XWorkException;
import com.opensymphony.xwork2.config.Configuration;
import com.opensymphony.xwork2.config.entities.ActionConfig;
import com.opensymphony.xwork2.config.entities.ResultConfig;
import com.opensymphony.xwork2.inject.Inject;


/**
 * supoort for zero configuration action development
 * @author Justin
 *
 */
public class RedworkUnkonwnHandler implements UnknownHandler {

	private static final Logger LOG = LoggerFactory.getLogger(RedworkUnkonwnHandler.class);
	protected Configuration configuration;
	protected ObjectFactory objectFactory;
	protected String parentPackage;
	protected String basePackage;
	protected String baseDir;
	protected RedworkEngine redworkEngine;
	
	/**
	 * 
	 * @param configuration
	 * @param objectFactory
	 * @param parentPackage	java包
	 * @param basePackage	struts2包
	 * @param baseDir		页面目录
	 */
	@Inject
	public RedworkUnkonwnHandler(
			RedworkEngine redworkEngine,
			Configuration configuration,
			ObjectFactory objectFactory,
			@Inject("redwork.parentPackage") String parentPackage,
			@Inject("redwork.basePackage") String basePackage,
			@Inject("redwork.baseDir") String baseDir) {
		
		this.redworkEngine = redworkEngine;
		this.configuration = configuration;
		this.objectFactory = objectFactory;
		this.parentPackage = parentPackage;
		this.basePackage = basePackage;
		this.baseDir = baseDir;
	}

	/**
	 * 处理未知Action
	 */
	@Override
	public ActionConfig handleUnknownAction(String namespace, String actionName)
			throws XWorkException {
		
		int splitIndex = actionName.indexOf('-');
		if(splitIndex != -1){
			// action
			String actionClassName = StringUtils.capitalize(actionName.substring(0, splitIndex)) + "Action";
			String actionMethodName = actionName.substring(splitIndex + 1);
			
			String className = String.format("%s%s.%s", basePackage, namespace.replace('/', '.'), actionClassName);
			try {
				Class.forName(className);
			} catch (ClassNotFoundException e) {
				// Action class not found, try to render ftl page
				return redworkEngine.getPageRenderingActionConfig(namespace, actionName);
			}
			
			// pkgname, name, classname
			ActionConfig config = new ActionConfig.Builder(namespace, actionName, className)
			.methodName(actionMethodName)
			.addInterceptors(redworkEngine.getDefaultIntercepterStack(parentPackage))
			.build();
			
			return config;
		}
		
		return redworkEngine.getPageRenderingActionConfig(namespace, actionName);
		
	}

	/**
	 * 使用Redwork风格结果
	 * 1. "/{page}" 渲染当前namespace下的ftl页面
	 * 2. "/{action}-{method}.action" action跳转
	 * 3. "json" json结果类型
	 * 4. HTTP header Accept为"application/json"的 json结果类型
	 */
	@Override
	public Result handleUnknownResult(ActionContext actionContext,
			String actionName, ActionConfig actionConfig, String resultCode)
			throws XWorkException {
		
		// must be an implementation of RedworkAction interface
		if(!redworkEngine.isRedworkAction(actionConfig.getClassName())){
			return null;
		}
		
		if (resultCode.endsWith(".action")) {
			/**
			 * action redirect
			 */
			LOG.debug("action redirection");
			
			ResultConfig redirectResultCfg = new ResultConfig.Builder(resultCode, ServletActionRedirectResult.class.getName()).build();
			
			try {
				return objectFactory.buildResult(redirectResultCfg, actionContext.getContextMap());
			} catch (Exception e) {
				throw new XWorkException("Unable to build redwork redirect result", e, redirectResultCfg);
			}
			
		}else if("json".equals(resultCode) || redworkEngine.requireJson() || redworkEngine.jsonAnnotationPresents(actionConfig)){
			
			/**
			 * render json
			 */
			return redworkEngine.builderJsonResult(actionConfig);
			
		}else if("input".equals(resultCode)){
			
			ActionMapping mapping = redworkEngine.getActionMappingFromReferer();
			ResultConfig resultConfig = new ResultConfig.Builder(mapping.getName(), ActionChainResult.class.getName())
			.addParam("actionName", mapping.getName())
			.addParam("namespace", mapping.getNamespace())
			.build();
			
			try {
				return objectFactory.buildResult(resultConfig, ActionContext.getContext().getContextMap());
			} catch (Exception e) {
				throw new XWorkException("Unable to build redwork json result", e, resultConfig);
			}
			
		}else{
			/**
			 * render ftl page
			 */
			
			// build the freemarker result config
			final String packageName = actionConfig.getPackageName().toLowerCase();
			StringBuilder sb = new StringBuilder().append(baseDir);
			if(!resultCode.startsWith("/")){
				sb.append(packageName).append("/");
			}
			sb.append(resultCode).append(".ftl");
			final String pageLocation = sb.toString();
			ResultConfig genericFreemarkerResult = new ResultConfig.Builder(resultCode, FreemarkerResult.class.getName())
			.addParam("location", pageLocation).build();

			try {
				return objectFactory.buildResult(genericFreemarkerResult, actionContext.getContextMap());
			} catch (Exception e) {
				throw new XWorkException("Unable to build redwork freemarker result", e, genericFreemarkerResult);
			}
		}
		
	}

	/**
	 * keep it returning null...
	 */
	@Override
	public Object handleUnknownActionMethod(Object action, String methodName)
			throws NoSuchMethodException {
		return null;
	}
}
